Skip to content

Conversation

@faogustavo
Copy link
Collaborator

@faogustavo faogustavo commented Dec 24, 2025

  • Fixed index emission on SelectableLazyColumn when the selection changes by the speed search
  • Created 'EmptySpeedSearchMatcher' to easily identify when the filter text is empty
    • This matches is automatically returned in the SpeedSearchState.matcher when the text is empty
  • Added 'dismissOnLoseFocus' to SpeedSearchArea to keep the filter when the focus is left from the component
  • Added 'currentMatcher' to the 'SpeedSearchState', allowing the user use it for filtering purposes
  • Created convenience function on top of 'SpeedSearchMatcher' to check if the given text matches or not
  • Created convenience functions to support filtering collections based on the speed search matcher
  • Added a new page in the samples to test all SpeedSearch components and their variants
  • Added documentation for a bunch of public APIs that were missing

Evidences

Filter Speed Search
Screen Recording 2025-12-23 at 22 09 11

Release notes

⚠️ Important Changes

  • Added 'dismissOnLoseFocus' property to SpeedSearchArea, allowing users to choose if they want to keep the input visible on lose focus
    • This is important for cases that you want to use SpeedSearch as a filter

New features

  • Added support for filtering Collections using SpeedSearchMatcher.
    • Check the '.filter()' extension functions available in the 'org.jetbrains.jewel.foundation.search' package

Bug fixes

  • Fixed an issue that was not triggering the 'onSelectedIndexesChange' call when the selection gets changed by the SpeedSearch

Note

Introduces filtering-friendly SpeedSearch and related API updates, plus selection emission fixes and samples/tests.

  • Adds EmptySpeedSearchMatcher, CharSequence.matches, and Iterable.filter(...) helpers; SpeedSearchMatcher.matches now accepts CharSequence
  • Expands SpeedSearchArea: new overloads with external SpeedSearchState, rememberSpeedSearchState, and dismissOnLoseFocus flag; exposes SpeedSearchState.currentMatcher, isVisible setter, and internal textFieldState; batching + matcher caching for performance
  • Updates SpeedSearchableLazyColumn scroll logic to sync with search/selection; wires attach flows; improves index sync when search is cleared
  • Fixes SelectableLazyColumn to emit onSelectedIndexesChange when SpeedSearch changes selection and to resync lastActiveItemIndex
  • Adds showcase page (SpeedSearches), icon, and comprehensive UI/tests (including filtering behavior and focus-dismiss cases)

Written by Cursor Bugbot for commit 9bfb03a. This will update automatically on new commits. Configure here.

@faogustavo faogustavo force-pushed the gv/JEWEL-1204 branch 2 times, most recently from dc7354c to f2d590a Compare January 5, 2026 14:29
Copy link
Collaborator

@wellingtoncosta wellingtoncosta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just small suggestions, LGTM overall.

@faogustavo
Copy link
Collaborator Author

FYI, I've added a warning banner to the new screen mentioning that the speed search component is not intended to this behavior. I'm also working on JEWEL-849, but it is taking a bit longer than I expected.

image

@ExperimentalJewelApi
@ApiStatus.Experimental
@Deprecated("Kept for binary compatibility", level = DeprecationLevel.HIDDEN)
public fun SpeedSearchArea(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we haz KDoc plz

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind this is hidden

- Fixed index emission on SelectableLazyColumn when the selection changes by the speed search
- Created 'EmptySpeedSearchMatcher' to easily identify when the filter text is empty
  - This matches is automatically returned in the SpeedSearchState.matcher when the text is empty
- Added 'dismissOnLoseFocus' to SpeedSearchArea to keep the filter when the focus is left from the component
- Added 'currentMatcher' to the 'SpeedSearchState', allowing the user use it for filtering purposes
- Created convenience function on top of 'SpeedSearchMatcher' to check if the given text matches or not
- Created convenience functions to support filtering collections based on the speed search matcher
Snapshot.withMutableSnapshot {
textFieldState.edit { delete(0, length) }
searchText = ""
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clearSearch doesn't reset currentMatcher causing stale filter state

Medium Severity

The clearSearch() method clears textFieldState and searchText within a Snapshot.withMutableSnapshot block but does not update currentMatcher to EmptySpeedSearchMatcher. The currentMatcher is only updated asynchronously by the flow running on a background dispatcher. This creates a race condition where consumers using currentMatcher for filtering (like derivedStateOf { list.filter(speedSearchState.currentMatcher) }) may see stale filter results after the search is cleared, causing a brief visual flicker where the list stays filtered even though the search input has disappeared.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants